package com.demo.crm.security.session;

import com.demo.crm.model.UserToken;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpSession;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 模仿spring的实现
 *
 * @author tanshuai
 * @see org.springframework.security.core.session.SessionRegistryImpl
 */
@Slf4j
public class SessionRegistryImplExtend {

    private static ConcurrentMap<String, Set<String>> sessionIdCaches = new ConcurrentHashMap<String, Set<String>>();
    private final static Map<String, SessionInformation> sessions = new ConcurrentHashMap<String, SessionInformation>();

    private int maxSessionNum = 1;

    private boolean throwExceptionIfOver = true;

    public void setMaxSessionNum(int maxSessionNum) {
        if (maxSessionNum < 1) {
            maxSessionNum = 1;// 最小值必须不小于1
        }
        this.maxSessionNum = maxSessionNum;
    }

    public void setThrowExceptionIfOver(boolean throwExceptionIfOver) {
        this.throwExceptionIfOver = throwExceptionIfOver;
    }

    public void register(UserToken userToken, HttpSession session) {
        String key = userToken.getUserKey();

        Set<String> sessionIds = sessionIdCaches.get(key);
        if (sessionIds == null) {
            sessionIds = new CopyOnWriteArraySet<String>();
            Set<String> finalSessionIds = sessionIdCaches.putIfAbsent(key, sessionIds);
            if (finalSessionIds != null) {
                sessionIds = finalSessionIds;
            }
        }

        String sessionId = session.getId();
        if (sessionIds.size() >= maxSessionNum) {
            if (throwExceptionIfOver) {
                // 使当前session过期，并抛出异常
                session.invalidate();
                throw new IllegalStateException("不可重复登录");
            } else {
                // 踢出第一个
                sessionIds.remove(sessionId);
                SessionInformation sessionInformation = sessions.remove(sessionId);
                if (sessionInformation != null) {
                    HttpSession oldSession = sessionInformation.getSession();
                    if (oldSession != null) {
                        oldSession.invalidate();
                    }
                }
                log.info("踢出前面已登录的重复用户:{}", key);
            }
        }

        sessions.put(sessionId, new SessionInformation(key, session));
        sessionIds.add(sessionId);
    }

    public void remove(UserToken userToken) {
        if (userToken == null) {
            return;
        }
        Set<String> sessionIds = sessionIdCaches.remove(userToken.getUserKey());
        if (sessionIds == null || sessionIds.isEmpty()) {
            return;
        }

        for (String sessionId : sessionIds) {
            SessionInformation sessionInformation = sessions.remove(sessionId);
            if (sessionInformation != null && sessionInformation.getSession() != null) {
                sessionInformation.getSession().invalidate();
            }
        }
    }

    public static void onSessionDestroyed(String sessionId) {
        log.debug("session destroyed event handle");

        SessionInformation sessionInformation = sessions.remove(sessionId);
        if (sessionInformation == null) {
            return;
        }

        String key = sessionInformation.getKey();
        log.debug("删除session缓存:{}", key);

        Set<String> sessionIds = sessionIdCaches.get(key);
        if (sessionIds == null) {
            return;
        }
        sessionIds.remove(sessionId);

        if (sessionIds.isEmpty()) {
            sessionIdCaches.remove(key);
        }
    }
}

